package middleware

import (
	"context"
	"errors"
	"fmt"
	libredis "github.com/go-redis/redis"
	"reflect"
	"rosesbff/internal/data/redis"
	"strings"

	"rosesbff/internal/biz"

	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/transport"
	"github.com/go-kratos/kratos/v2/transport/http"
	"github.com/google/wire"
)

var ProviderSet = wire.NewSet(
	NewMiddleware,
)

type Middleware struct {
	account *biz.Account
	redis   *redis.Redis
}

func NewMiddleware(
	account *biz.Account,
	redis *redis.Redis,
) (*Middleware, error) {
	return &Middleware{
		account: account,
		redis:   redis,
	}, nil
}

// statusCodeMessage 替换响应的数据状态码和描述语
func (s *Middleware) statusCodeMessage(
	handler middleware.Handler,
	ctx context.Context,
	req any,
	statusCode int64,
	message string,
) (any, error) {
	reply, err := handler(ctx, req)
	if err != nil {
		return nil, err
	}
	value := reflect.New(reflect.TypeOf(reply).Elem())
	response := value.Elem()
	for index := range response.NumField() {
		field := response.Field(index)
		switch field.Type().Kind() {
		case reflect.Int64:
			field.Set(reflect.ValueOf(statusCode))
		case reflect.String:
			field.Set(reflect.ValueOf(message))
		default:
		}
	}
	// response.FieldByName("StatusCode").Set(reflect.ValueOf(statusCode))
	// response.FieldByName("Message").Set(reflect.ValueOf("登录过期"))
	return value.Interface(), nil
}

// Cors 跨域请求
func (s *Middleware) Cors(handler middleware.Handler) middleware.Handler {
	return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
		if tr, ok := transport.FromServerContext(ctx); ok {
			if trans, okt := tr.(*http.Transport); okt {
				// request := trans.Request()
				response := trans.Response()
				respHeader := response.Header()
				respHeader.Set("Access-Control-Allow-Origin", "*")
				respHeader.Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
				respHeader.Set("Access-Control-Allow-Headers", "Content-Type")
				respHeader.Set("Access-Control-Max-Age", "86400")
				respHeader.Set("Access-Control-Allow-Credentials", "false")
			}
		}
		return handler(ctx, req)
	}
}

// Login 登录鉴权
func (s *Middleware) Login(handler middleware.Handler) middleware.Handler {
	return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
		if tr, ok := transport.FromServerContext(ctx); ok {
			if trans, okt := tr.(*http.Transport); okt {
				request := trans.Request()
				path := request.URL.Path
				if strings.Contains(path, "/inner/") {
					token := request.Header.Get("Authorization")
					if token == "" {
						return s.statusCodeMessage(handler, ctx, req, 401, "缺少凭证")
					}
					claims, err := s.account.VerifyLoginToken(request.Context(), token)
					if err != nil {
						if strings.Contains(err.Error(), "token is expired") {
							return s.statusCodeMessage(handler, ctx, req, 401, "登录过期")
						}
						return nil, err
					}
					if claims.UserId <= 0 {
						return s.statusCodeMessage(handler, ctx, req, 401, "非法请求")
					}
					ctx = context.WithValue(ctx, "UserId", claims.UserId)
					ctx = context.WithValue(ctx, "Category", claims.Category)
					if claims.OriginUserId > 0 { // 复核员
						cmd := s.redis.Client().HGet("verifier:part_ticket", fmt.Sprintf("%d", claims.OriginUserId))
						if err = cmd.Err(); err != nil {
							if errors.Is(err, libredis.Nil) {
								return s.statusCodeMessage(handler, ctx, req, 401, "登录过期")
							}
							return nil, err
						}
					}
				}
				// query := request.URL.RawQuery
				// bts := bytes.NewBuffer(nil)
				// _, err = io.Copy(bts, request.Body)
				// if err != nil {
				// 	return nil, err
				// }
				// request.Body = io.NopCloser(bytes.NewBuffer(bts.Bytes()))
			}
		}
		return handler(ctx, req)
	}
}

// RbacPermit 鉴定RBAC权限
func (s *Middleware) RbacPermit(handler middleware.Handler) middleware.Handler {
	return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
		if tr, ok := transport.FromServerContext(ctx); ok {
			if trans, okt := tr.(*http.Transport); okt {
				request := trans.Request()
				path := request.URL.Path
				if strings.Contains(path, "/inner/rbac/") {
					category := s.account.GetUserCategory(ctx)
					if category > 0 {
						userId := s.account.GetUserId(ctx)
						method := strings.ToUpper(request.Method)
						has := s.account.VerifyUserRbacPermit(userId, method, request.URL.Path)
						if !has {
							return s.statusCodeMessage(handler, ctx, req, 403, "您没有该权限")
						}
					}
				}
			}
		}
		return handler(ctx, req)
	}
}
